package com.shockweb.common.calculate.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.shockweb.common.arithmetic.Stack;
import com.shockweb.common.calculate.api.IParameter;

/**
 * 简单表达式计算，支持+,-,*,/,()等，支持参数
 * @author pengminghua
 *
 */
public class SimpleCalculate{

	private final static int IsBig = 0;
	private final static int IsSmall = 1;
	private final static int IsEqual = 2;
	private final static HashMap<String,Integer> optr = new HashMap<String,Integer>();
	private final static int optrLen = 2;
	private int index = 0;
	private List<String> keys = new ArrayList<String>(50);
	private Map<String,Object> params = new HashMap<String,Object>();
	private Stack<Object> optrStack = new Stack<Object>();
	private Stack<Object> opndStack = new Stack<Object>();
	private int divScale = 2;
	private IParameter parameter = null;
	/**
	 * 给表达式中的变量赋值
	 * @param key
	 * @param value
	 */
	public void putParam(String key,Object value){
		if(value!=null){
			if(value instanceof BigDecimal){
				params.put(key,value);
			}else if(value instanceof Double){
				params.put(key,new BigDecimal((Double)value));
			}else if(value instanceof Float){
				params.put(key,new BigDecimal((Float)value));
			}else if(value instanceof Integer){
				params.put(key,new BigDecimal((Integer)value));
			}else if(value instanceof Long){
				params.put(key,new BigDecimal((Long)value));
			}else if(value instanceof BigDecimal){
				params.put(key,(BigDecimal)value);
			}else if(value instanceof BigInteger){
				params.put(key,new BigDecimal((BigInteger)value));
			}else if(isString(value)){
				params.put(key,value);
			}else{
				params.put(key,new BigDecimal(value.toString()));
			}
		}
	}

	
	/**
	 * 设置参数
	 * @param params
	 */
	public void setParams(Map<String,Object> params){
		this.params = params;
	}
	
	static{
		optr.put("+",Integer.valueOf("4948"));
		optr.put("-",Integer.valueOf("4948"));
		optr.put("*",Integer.valueOf("5150"));
		optr.put("/",Integer.valueOf("5150"));
		optr.put("%",Integer.valueOf("5150"));		
		optr.put("\\",Integer.valueOf("5150"));
		optr.put(">",Integer.valueOf("4746"));
		optr.put(">=",Integer.valueOf("4746"));
		optr.put("<",Integer.valueOf("4746"));
		optr.put("<=",Integer.valueOf("4746"));
		optr.put("==",Integer.valueOf("4746"));
		optr.put("!=",Integer.valueOf("4746"));
		optr.put("#",Integer.valueOf("0"));
		optr.put("(",Integer.valueOf("199"));
		optr.put(")",Integer.valueOf("9901"));
		optr.put("||",Integer.valueOf("4544"));
		optr.put("&&",Integer.valueOf("4544"));
		optr.put("&|",Integer.valueOf("4544"));
		optr.put("|&",Integer.valueOf("4544"));
		optr.put(">>",Integer.valueOf("5352"));
		optr.put("<<",Integer.valueOf("5352"));
	}
	
	/**
	 * 创建表达式对象
	 * @param expression
	 * @param parameter
	 */
	public SimpleCalculate(String expression,IParameter parameter){
		this.parameter = parameter;
		createExpressionKeys(new StringBuffer(expression.replaceAll(" ","")));
	}
	
	/**
	 * 创建表达式对象
	 * @param expression
	 * @param parameter
	 * @param divScale
	 */
	public SimpleCalculate(String expression,IParameter parameter,int divScale){
		this.parameter = parameter;
		this.divScale = divScale;
		createExpressionKeys(new StringBuffer(expression.replaceAll(" ","")));
	}
	
	/**
	 * 创建表达式对象
	 * @param expression
	 * @param parameter
	 */
	public SimpleCalculate(StringBuffer expression,IParameter parameter){
		this.parameter = parameter;
		createExpressionKeys(expression);
	}
	
	/**
	 * 创建表达式对象
	 * @param expression
	 * @param parameter
	 * @param divScale
	 */
	public SimpleCalculate(StringBuffer expression,IParameter parameter,int divScale){
		this.parameter = parameter;
		this.divScale = divScale;
		createExpressionKeys(expression);
	}
	
	/**
	 * 设置除法运算时的精度
	 * @return int
	 */
	public int getDivScale(){
		return divScale;
	}
	
	/**
	 * 设置除法运算时的精度
	 * @param divScale
	 */
	public void setDivScale(int divScale){
		this.divScale = divScale;
	}
	
	/**
	 * 床架表达式key
	 * @param expression
	 */
	private void createExpressionKeys(StringBuffer expression){
		String str = "";
		int index = 0;
		keys.clear();
		String prev = null;
		String tmp = null;
		while(index<expression.length()){
			int startIndex = index;
			int endIndex = isFullOptr(index,expression);
			if(endIndex==-1){
				while(endIndex==-1){
					index ++ ;
					endIndex = isFullOptr(index,expression);
				}
				str = expression.substring(startIndex,index);
			}else if(endIndex==-2){
				return;
			}else{
				str = expression.substring(index,endIndex);
				index = endIndex;
			}
			if((str.equals("-")) && (isOptr(prev) || keys.isEmpty())){
				tmp = str;
			}else if(str.startsWith("!") && !str.startsWith("!=") && str.length()>1){
				if(tmp!=null){
					keys.add("(");
					keys.add("0");
					keys.add("-");
					keys.add("(");
					keys.add("0");
					keys.add("&|");
					keys.add(str.substring(1, str.length()));
					keys.add(")");
					keys.add(")");
				}else{
					keys.add("(");
					keys.add("0");
					keys.add("&|");
					keys.add(str.substring(1, str.length()));
					keys.add(")");
				}
				tmp = null;
			}else if(str !=null){
				if(tmp!=null){
					keys.add("-" + str);
				}else{
					keys.add(str);
				}
				tmp = null;
			}
			prev = str;
		}
		keys.add("#");
	}

	
	/**
	 * 判断expression第index个字符后的key是否为操作符
	 * @param index
	 * @param expression
	 * @return int
	 */
	private int isFullOptr(int index,StringBuffer expression){
		int i = 0;
		int expLen = expression.length();
		if(index>=expLen){
			return -2;
		}
		if(expression.charAt(index)=='"'){
			return expression.indexOf("\"",index+1)+1;
		}
		int endIndex = getRealLen(index,expLen,i);
		String currStr = expression.substring(index,endIndex);
		while(!isOptr(currStr) && i<optrLen){
			i++;
			endIndex = getRealLen(index,expLen,i);
			currStr = expression.substring(index,endIndex);
		}
		if(i==optrLen){
			return -1;
		}else{
			return endIndex;
		}
	}
	
	/**
	 * 返回实际的长度
	 * @param index
	 * @param len
	 * @param i
	 * @return int
	 */
	private int getRealLen(int index,int len,int i){
		int rtn = 0;
		if(index<len){
			if(index + optrLen - i>len){
				i++;
				rtn = getRealLen(index,len,i);
				if(rtn <0) rtn = 0;
			}else{
				rtn = index + optrLen - i;
			}
		}
		return rtn;
	}
	
	/**
	 * 判断key是否为操作符
	 * @param x
	 * @return boolean
	 */
	private boolean isOptr(String key){
		return optr.containsKey(key);
	}

	/**
	 *  比较运算符的优先级
	 * @param optr1
	 * @param optr2
	 * @return int
	 */
	private int precede(String optr1,String optr2){
		int leftOptr = (optr.get(optr1)).intValue()/100;
		int rightOptr = (optr.get(optr2)).intValue()%100;
		if(leftOptr>rightOptr){
			return IsBig;
		}else if(leftOptr<rightOptr){
			return IsSmall;
		}else{
			return IsEqual;
		}
	}
	

	/**
	 * 计算整个表达式
	 * @return Object
	 * @throws Exception
	 */
	public Object eval()throws Exception{
		index = 0;
		optrStack.clear();
		opndStack.clear();
		optrStack.push("#");
		String w = readNextKey();
		while(w!=null && !("#".equals(w) && "#".equals(optrStack.getTop()))){
			if(!isOptr(w)){
				opndStack.push(w);
				w = readNextKey();
			}else{
				int p = precede((String)optrStack.getTop(),w);
				if(IsSmall == p){
					optrStack.push(w);
					w = readNextKey();
				}else if(IsEqual == p){
					optrStack.pop();
					w = readNextKey();
				}else if(IsBig == p){
					String theta = (String)optrStack.pop();
					Object b = opndStack.pop();
					Object a = opndStack.pop();
					opndStack.push(operate(a,theta,b));
				}
			}
		}
		if(opndStack.size()!=1){
			throw new Exception("Missing operator"); 
		}else{
			return getValue(opndStack.getTop());
		}
	}
	
	/**
	 * 判断是否为字符串
	 * @param key
	 * @return
	 */
	private boolean isString(Object key){
		if(key!=null && key instanceof String){
			String k = (String)key;
			if(k.length()>=2 && k.endsWith("\"") && k.startsWith("\"")){
				return true;
			}
		}
		return false;
	}

	/**
	 * 获取key的对应的值
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private Object getValue(Object key)throws Exception{
		if(key instanceof BigDecimal){
			return (BigDecimal)key;
		}else if(isString(key)){
			String k = (String)key;
			return k.substring(1, k.length()-1);
		}else if("null".equals(key)){
			return null;
		}
		if(key instanceof String){
			try{
				return new BigDecimal((String)key);
			}catch(Exception e){
			}
			if(!params.isEmpty() && params.containsKey(key)){
				return params.get(key);
			}
		}
		if(parameter!=null){
			Object rtn = convert(parameter.getParam((String)key));
			params.put(key.toString(), rtn);
			return rtn;
		}else{
			throw new Exception("Syntax error on token " + key);
		}
	}
	
	/**
	 * 传换值
	 * @param value
	 * @return BigDecimal
	 */
	protected Object convert(Object value){
		if(value!=null){
			if(value instanceof Double){
				return new BigDecimal((Double)value);
			}else if(value instanceof Float){
				return new BigDecimal((Float)value);
			}else if(value instanceof Integer){
				return new BigDecimal((Integer)value);
			}else if(value instanceof Long){
				return new BigDecimal((Long)value);
			}else if(value instanceof BigDecimal){
				return value;
			}else if(value instanceof BigInteger){
				return new BigDecimal((BigInteger)value);
			}else if(value instanceof String){
				return (String)value;
			}else{
				return new BigDecimal(value.toString());
			}
		}
		return null;
	}
	
	/**
	 * 数字类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object bigDecimalOperate(BigDecimal va,String theta,BigDecimal vb)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = va.add(vb);
		}else if("-".equals(theta)){
			rtn = va.subtract(vb);
		}else if("*".equals(theta)){
			rtn = va.multiply(vb);
		}else if("/".equals(theta)){
			rtn = va.divide(vb,divScale,BigDecimal.ROUND_HALF_UP);
		}else if("%".equals(theta)){
			rtn = new BigDecimal(va.intValue()%vb.intValue());
		}else if(">".equals(theta)){
			if(va.compareTo(vb)==1)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if(">=".equals(theta)){
			if(va.compareTo(vb)==-1)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			if(va.compareTo(vb)==-1)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			if(va.compareTo(vb)==1)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if("==".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if("||".equals(theta)){
			if(va.doubleValue()!=0 || vb.doubleValue()!=0){
				rtn = new BigDecimal(-1);
			}else{
				rtn = new BigDecimal(0);
			}
		}else if("&&".equals(theta)){
			if(va.doubleValue()==0 || vb.doubleValue()==0){
				rtn = new BigDecimal(0);
			}else{
				rtn = new BigDecimal(-1);
			}
		}else if("&|".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if("|&".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if(">>".equals(theta)){
			rtn = new BigDecimal(va.longValue()>>vb.longValue());
		}else if("<<".equals(theta)){
			rtn = new BigDecimal(va.longValue()<<vb.longValue());
		}else{
			throw new Exception("Illegal arithmetic sign");
		}
		return rtn;
	}
	
	
	/**
	 * 字符类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object stringOperate(String va,String theta,String vb)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + va + vb + "\"";
		}else if(">".equals(theta)){
			if(va.compareTo(vb)==1)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if(">=".equals(theta)){
			if(va.compareTo(vb)==-1)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			if(va.compareTo(vb)==-1)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			if(va.compareTo(vb)==1)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else if("==".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(-1);
			else
				rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			if(va.compareTo(vb)==0)
				rtn = new BigDecimal(0);
			else
				rtn = new BigDecimal(-1);
		}else{
			throw new Exception("Illegal arithmetic sign");
		}
		return rtn;
	}
	
	/**
	 * 数字与字符类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object stringOperate(BigDecimal va,String theta,String vb)throws Exception{
		Object rtn;
		if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}

	
	/**
	 * 字符与数字类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object stringOperate(String va,String theta,BigDecimal vb)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + va + vb + "\"";
		}else if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}

	/**
	 * null类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object nullOperate(String theta)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + null + null + "\"";
		}else if(">".equals(theta)){
			rtn = new BigDecimal(0);
		}else if(">=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("==".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(0);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}
	
	/**
	 * 数字与null类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object nullOperate(BigDecimal va,String theta)throws Exception{
		Object rtn;
		if(">".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if(">=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}
	
	/**
	 * null与数字类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object nullOperate(String theta,BigDecimal vb)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + null + vb + "\"";
		}else if(">".equals(theta)){
			rtn = new BigDecimal(0);
		}else if(">=".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("<".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("<=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}
	
	/**
	 * null与 字符类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object null2Operate(String theta,String vb)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + null + vb + "\"";
		}else if(">".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if(">=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}
	
	/**
	 * 字符与null类型计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object nullOperate(String va,String theta)throws Exception{
		Object rtn;
		if("+".equals(theta)){
			rtn = "\"" + va + null + "\"";
		}else if(">".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if(">=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else if("<".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("<=".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("==".equals(theta)){
			rtn = new BigDecimal(0);
		}else if("!=".equals(theta)){
			rtn = new BigDecimal(-1);
		}else{
			if(optr.containsKey(theta)){
				throw new Exception("Syntax error on token " + theta);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}
		return rtn;
	}
	
	
	/**
	 * 简单二元计算
	 * @param a
	 * @param theta
	 * @param b
	 * @return BigDecimal
	 * @throws LangException
	 */
	private Object operate(Object a,String theta,Object b)throws Exception{
		Object rtn;
		Object oa = getValue(a);
		Object ob = getValue(b);
		if(oa==null){
			if(ob==null){
				rtn = nullOperate(theta);
			}else if(ob instanceof BigDecimal){
				rtn = nullOperate(theta,(BigDecimal)ob);
			}else if(ob instanceof String){
				rtn = null2Operate(theta,(String)ob);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}else if(oa instanceof BigDecimal){
			if(ob==null){
				rtn = nullOperate((BigDecimal)oa,theta);
			}else if(ob instanceof BigDecimal){
				rtn = bigDecimalOperate((BigDecimal)oa,theta,(BigDecimal)ob);
			}else if(ob instanceof String){
				rtn = stringOperate((BigDecimal)oa,theta,(String)ob);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}else if(oa instanceof String){
			if(ob==null){
				rtn = nullOperate((String)oa,theta);
			}else if(ob instanceof BigDecimal){
				rtn = stringOperate((String)oa,theta,(BigDecimal)ob);
			}else if(ob instanceof String){
				rtn = stringOperate((String)oa,theta, (String)ob);
			}else{
				throw new Exception("Illegal arithmetic sign");
			}
		}else{
			throw new Exception("Illegal arithmetic sign");
		}
		return rtn;
	}

	
	/**
	 * 读取下一个key
	 * @return Object
	 */
	private String readNextKey(){
		if(index>=keys.size()){
			return null;
		}else{
			return keys.get(index++);
		}
	}
}
